package blade.migrate.core; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.CatchClause; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.ImportDeclaration; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.TypeDeclaration; /** * Parses a java file and provides some methods for finding search results */ public class JavaFileChecker { private static final String[] SERVICE_API_SUFFIXES = { "LocalService", "LocalServiceUtil", "LocalServiceWrapper", "Service", "ServiceUtil", "ServiceWrapper", }; private final CompilationUnit _ast; private final File _file; private final FileHelper _fileHelper; /** * initialize the Checker * @param file java file */ public JavaFileChecker(File file) { _file = file; _fileHelper = new FileHelper(); try { _ast = CUCache.getCU(file, getJavaSource()); } catch (Exception e) { throw new IllegalArgumentException(e); } } protected SearchResult createSearchResult(int startOffset, int endOffset, int startLine, int endLine, boolean fullMatch) { return new SearchResult(_file, startOffset, endOffset, startLine, endLine, fullMatch); } public List<SearchResult> findCatchExceptions(final String[] exceptions) { final List<SearchResult> searchResults = new ArrayList<>(); _ast.accept(new ASTVisitor() { @Override public boolean visit(CatchClause node){ String exceptionTypeName = node.getException().getType().toString(); boolean retVal = false; for (String exceptionType : exceptions) { if ( exceptionTypeName.equals(exceptionType)){ final int startLine = _ast.getLineNumber(node.getException().getStartPosition()); final int startOffset = node.getException().getStartPosition(); int endLine = _ast.getLineNumber(node.getException().getStartPosition() + node.getException().getLength()); int endOffset = node.getException().getStartPosition() + node.getException().getLength(); searchResults .add(createSearchResult(startOffset, endOffset, startLine, endLine, true)); retVal = true; } } return retVal; } }); return searchResults; } public List<SearchResult> findImplementsInterface(final String interfaceName){ final List<SearchResult> searchResults = new ArrayList<>(); _ast.accept(new ASTVisitor() { @Override public boolean visit(TypeDeclaration node) { ITypeBinding[] superInterfaces = null; if (node.resolveBinding() != null) { superInterfaces = node.resolveBinding().getInterfaces(); if (superInterfaces != null && superInterfaces.length > 0) { if (superInterfaces[0].getName() .equals(interfaceName)) { int startLine = _ast.getLineNumber( node.getName().getStartPosition()); int startOffset = node.getName().getStartPosition(); int endLine = _ast.getLineNumber( node.getName().getStartPosition() + node.getName().getLength()); int endOffset = node.getName().getStartPosition() + node.getName().getLength(); searchResults .add(createSearchResult(startOffset, endOffset, startLine, endLine, true)); } } } return true; } }); return searchResults; } public SearchResult findImport(final String importName) { final List<SearchResult> searchResults = new ArrayList<>(); _ast.accept(new ASTVisitor() { @Override public boolean visit(ImportDeclaration node) { if (importName.equals(node.getName().toString())) { int startLine = _ast.getLineNumber(node.getName() .getStartPosition()); int startOffset = node.getName().getStartPosition(); int endLine = _ast.getLineNumber(node.getName() .getStartPosition() + node.getName().getLength()); int endOffset = node.getName().getStartPosition() + node.getName().getLength(); searchResults.add(createSearchResult(startOffset, endOffset, startLine, endLine, true)); } return false; }; }); if (0 != searchResults.size()) { return searchResults.get(0); } return null; } public List<SearchResult> findMethodDeclaration( final String name, final String[] params) { final List<SearchResult> searchResults = new ArrayList<>(); _ast.accept(new ASTVisitor() { @Override public boolean visit(MethodDeclaration node) { boolean sameParmSize = true; String methodName = node.getName().toString(); List<?> parmsList = node.parameters(); if (name.equals(methodName) && params.length == parmsList.size()) { for (int i = 0; i < params.length; i++) { if (!(params[i].trim().equals(parmsList.get(i) .toString() .substring(0, params[i].trim().length())))) { sameParmSize = false; break; } } } else { sameParmSize = false; } if (sameParmSize) { final int startLine = _ast.getLineNumber(node.getName() .getStartPosition()); final int startOffset = node.getName().getStartPosition(); node.accept(new ASTVisitor() { @Override public boolean visit(Block node) { // SimpleName parent can not be MarkerAnnotation and // SimpleType // SingleVariableDeclaration node contains the // parms's type int endLine = _ast.getLineNumber(node .getStartPosition()); int endOffset = node.getStartPosition(); searchResults .add(createSearchResult(startOffset, endOffset, startLine, endLine, true)); return false; }; }); } return false; } }); return searchResults; } /** * find the method invocations for a particular method on a given type or expression * * @param typeHint the type hint to use when matching expressions * @param expressionValue the expression only value (no type hint) * @param methodName the method name * @return search results */ @SuppressWarnings("unchecked") public List<SearchResult> findMethodInvocations( final String typeHint, final String expressionValue, final String methodName, final String[] methodParamTypes) { final List<SearchResult> searchResults = new ArrayList<>(); _ast.accept(new ASTVisitor() { @Override public boolean visit(MethodInvocation node) { final String methodNameValue = node.getName().toString(); final Expression expression = node.getExpression(); ITypeBinding type = null; if (expression != null) { type = expression.resolveTypeBinding(); } if ( ((methodName.equals(methodNameValue)) || ("*".equals(methodName))) && // if typeHint is not null it must match the type hint and ignore the expression // not strictly check the type and will check equals later ( (typeHint != null && type != null && type.getName().endsWith(typeHint)) || // with no typeHint then expressions can be used to match Static invocation (typeHint == null && expression != null && expression.toString().equals(expressionValue)))) { boolean argumentsMatch = false; if (methodParamTypes != null) { Expression[] argExpressions = ((List<Expression>)node.arguments()).toArray(new Expression[0]); if (argExpressions.length == methodParamTypes.length) { //args number matched boolean possibleMatch = true; // assume all types will match until we find otherwise boolean typeMatched = true; boolean typeUnresolved = false; for(int i = 0; i < argExpressions.length; i++) { Expression arg = argExpressions[i]; ITypeBinding argType = arg.resolveTypeBinding(); if (argType != null) { //can resolve the type if( argType.getName().equals(methodParamTypes[i])) { //type matched continue; } else { //type unmatched possibleMatch = false; typeMatched = false; break; } } else{ possibleMatch = false; //there are two cases : //typeUnresolved : means that all resolved type is matched and there is unsolved type , need to set fullMatch false //typeUnmatched : means that some resolved type is unmatched , no need to add SearchResult //do not add searchResults now , just record the state and continue //because there maybe unmatched type later which will break this case typeUnresolved = true; } } if( typeMatched && typeUnresolved ){ final int startOffset = expression.getStartPosition(); final int startLine = _ast.getLineNumber(startOffset); final int endOffset = node.getStartPosition() + node.getLength(); final int endLine = _ast.getLineNumber(endOffset); //can't resolve the type but args number matched , note that the last param is false searchResults.add(createSearchResult(startOffset, endOffset, startLine, endLine, false)); } if (possibleMatch) { argumentsMatch = true; } } } //any method args types is OK without setting methodParamTypes else { argumentsMatch = true; } if (argumentsMatch) { final int startOffset = expression.getStartPosition(); final int startLine = _ast.getLineNumber(startOffset); final int endOffset = node.getStartPosition() + node.getLength(); final int endLine = _ast.getLineNumber(endOffset); boolean isFullMatch = true; //endsWith but not equals if( typeHint != null && type != null && type.getName().endsWith(typeHint) && !type.getName().equals(typeHint) ) { isFullMatch = false; } searchResults.add(createSearchResult(startOffset, endOffset, startLine, endLine, isFullMatch)); } } return true; } }); return searchResults; } public SearchResult findPackage(final String packageName) { final List<SearchResult> searchResults = new ArrayList<>(); _ast.accept(new ASTVisitor() { @Override public boolean visit(PackageDeclaration node) { if (packageName.equals(node.getName().toString())) { int startLine = _ast.getLineNumber(node.getName() .getStartPosition()); int startOffset = node.getName().getStartPosition(); int endLine = _ast.getLineNumber(node.getName() .getStartPosition() + node.getName().getLength()); int endOffset = node.getName().getStartPosition() + node.getName().getLength(); searchResults.add(createSearchResult(startOffset, endOffset, startLine, endLine, true)); } return false; }; }); if (0 != searchResults.size()) { return searchResults.get(0); } return null; } public List<SearchResult> findQualifiedName(final String exception) { final List<SearchResult> searchResults = new ArrayList<>(); _ast.accept(new ASTVisitor() { @Override public boolean visit(QualifiedName node) { String qualifyName = node.getFullyQualifiedName(); boolean retVal = false; if (qualifyName.equals(exception)) { final int startLine = _ast .getLineNumber(node.getStartPosition()); final int startOffset = node.getStartPosition(); int endLine = _ast.getLineNumber( node.getStartPosition() + node.getLength()); int endOffset = node.getStartPosition() + node.getLength(); searchResults.add(createSearchResult(startOffset, endOffset, startLine, endLine, true)); retVal = true; } return retVal; } }); return searchResults; } public List<SearchResult> findServiceAPIs(final String[] serviceFQNPrefixes) { final List<SearchResult> searchResults = new ArrayList<>(); for (String prefix : serviceFQNPrefixes) { for (String suffix : SERVICE_API_SUFFIXES) { String serviceFQN = prefix + suffix; SearchResult importResult = findImport(serviceFQN); if (importResult != null) { searchResults.add(importResult); } String service = serviceFQN.substring( serviceFQN.lastIndexOf('.') + 1, serviceFQN.length()); searchResults.addAll( findMethodInvocations(null, service, "*", null)); searchResults.addAll( findMethodInvocations(service, null, "*", null)); } } return searchResults; } public List<SearchResult> findSuperClass(final String superClassName){ final List<SearchResult> searchResults = new ArrayList<>(); _ast.accept(new ASTVisitor() { @Override public boolean visit(TypeDeclaration node) { ITypeBinding superClass = null; if (node.resolveBinding() != null) { superClass = node.resolveBinding().getSuperclass(); if (superClass != null) { if (superClass.getName().equals(superClassName)) { int startLine = _ast.getLineNumber( node.getName().getStartPosition()); int startOffset = node.getName().getStartPosition(); int endLine = _ast.getLineNumber( node.getName().getStartPosition() + node.getName().getLength()); int endOffset = node.getName().getStartPosition() + node.getName().getLength(); searchResults .add(createSearchResult(startOffset, endOffset, startLine, endLine, true)); } } } return true; } }); return searchResults; } protected File getFile() { return _file; } protected char[] getJavaSource() { try { return _fileHelper.readFile(_file).toCharArray(); } catch (IOException e) { e.printStackTrace(); } return null; } }